package util

import (
	"bytes"
	"crypto/rand"
	"encoding/json"
	"errors"
	"fmt"
	"haodayizhi/global"
	mtype "haodayizhi/types"
	"log"
	"math/big"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"text/template"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/golang-jwt/jwt/v4"
	"github.com/google/uuid"
	"golang.org/x/crypto/bcrypt"
	"gopkg.in/gomail.v2"
)

// GenerateJWT 生成 JWT
func GenerateJWT(userID int64, email string) (string, error) {
	claims := jwt.MapClaims{
		"user_id": userID,
		"email":   email,
		"exp":     time.Now().Add(time.Duration(global.Config.Security.TokenExpiration) * time.Second).Unix(),
	}

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	signedToken, err := token.SignedString([]byte(global.Config.Security.JwtSecret))
	if err != nil {
		return "", err
	}

	return signedToken, nil
}

// ParseJWT 解析 JWT 并验证
func ParseJWT(tokenString string) (mtype.TokenConfig, error) {
	tokenConf := mtype.TokenConfig{}
	token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return tokenConf, errors.New("unexpected signing method")
		}
		return []byte(global.Config.Security.JwtSecret), nil
	})

	if err != nil {
		return tokenConf, err
	}

	claims, ok := token.Claims.(jwt.MapClaims)
	if !ok || !token.Valid {
		return tokenConf, errors.New("invalid token")
	}

	jsonString, err := json.Marshal(claims)
	if err != nil {
		return tokenConf, err
	}
	err = json.Unmarshal(jsonString, &tokenConf)
	if err != nil {
		return tokenConf, err
	}
	return tokenConf, nil
}

// 加密密码
func EncryPassWord(password string) (string, error) {
	hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
	if err != nil {
		return "", err
	}
	return string(hash), err
}

func CheckPassword(hashPassword, password string) bool {
	err := bcrypt.CompareHashAndPassword([]byte(hashPassword), []byte(password))
	if err != nil {
		LogMessage(LogTypeError, fmt.Sprintf("bcrypt.CompareHashAndPassword err: %s", err))
		return false
	}
	return err == nil
}

// 通用错误处理中间件
func CommonErrorHandler(c *gin.Context, message string, code int, result interface{}, err error) {
	response := mtype.ResponseType{}
	response.Code = code
	response.Message = message
	response.Result = nil
	LogMessage(LogTypeError, fmt.Sprintf("message: %s, code: %d, result: %v", message, code, result))
	c.JSON(code, response)
}
func GetRootDir() (string, error) {
	executable, err := os.Executable()
	if err != nil {
		return "", err
	}
	rootDir := filepath.Dir(filepath.Dir(executable)) // Move one level up from bin directory
	return rootDir, nil
}

// 获取到Token
func GetTokenInfo(c *gin.Context) (mtype.TokenConfig, error) {
	var tokenConfig mtype.TokenConfig
	response := mtype.ResponseType{}
	headInfo, exists := c.Get(global.LOG_USER)
	headInfoByte, err := json.Marshal(headInfo)
	if err != nil {
		LogMessage(LogTypeError, fmt.Sprintf("Failed to get user info: %v", err))
		response.Code = http.StatusInternalServerError
		response.Message = "Failed to get user info"
		c.JSON(http.StatusInternalServerError, response)
		return tokenConfig, err

	}
	err = json.Unmarshal(headInfoByte, &tokenConfig)
	if err != nil {
		LogMessage(LogTypeError, fmt.Sprintf("Failed to get user info: %v", err))
		response.Code = http.StatusInternalServerError
		response.Message = "Failed to get user info"
		c.JSON(http.StatusInternalServerError, response)
		return tokenConfig, err
	}
	if !exists {
		LogMessage(LogTypeError, fmt.Sprintf("Failed to get user info: %v", exists))
		response.Code = http.StatusInternalServerError
		response.Message = "Failed to get user info"
		c.JSON(http.StatusInternalServerError, response)
		return tokenConfig, err
	}
	return tokenConfig, nil
}

// 发送邮件

func SendEmail(emailInfo mtype.EmailSendContent) error {
	// 获取根目录
	rootDir, err := GetRootDir()
	if err != nil {
		return err
	}
	fmt.Println()
	// 读取模板文件
	tmpl, err := template.ParseFiles(rootDir + emailInfo.Template)
	if err != nil {
		return err
	}

	// 执行模板渲染
	var tpl bytes.Buffer
	if err := tmpl.Execute(&tpl, emailInfo.TemplateData); err != nil {
		return err
	}
	// 初始化邮件对象

	m := gomail.NewMessage()
	if emailInfo.From == "" {
		emailInfo.From = global.Config.Email.From // 发件人邮箱地址
	}
	m.SetAddressHeader("From", "1509939219@qq.com", emailInfo.From)
	if emailInfo.To == nil {
		emailInfo.To = strings.Split(global.Config.Email.To, ",") // 收件人邮箱地址
	}
	m.SetHeader("To", emailInfo.To...) // 收件人
	m.SetHeader("Cc", emailInfo.CC...) // 抄送
	// if emailInfo.Bcc == nil {
	// 	emailInfo.Bcc = strings.Split(global.Config.Email.BCC, ",") // 密送
	// }

	if emailInfo.Subject == "" {
		emailInfo.Subject = "测试邮件" // 邮件主题
	}

	m.SetHeader("Subject", emailInfo.Subject) // 邮件主题
	// m.SetBody("text/html", emailInfo.Body) // 邮件正文
	m.SetBody("text/html", tpl.String()) // 邮件正文

	// 设置 SMTP 发送邮件服务器地址和端口，以及邮箱账号和密码
	d := gomail.NewDialer(global.Config.Email.Host, global.Config.Email.Port, global.Config.Email.Username, global.Config.Email.Password)

	// 发送邮件
	if err := d.DialAndSend(m); err != nil {
		log.Println("发送邮件失败:", err)
		return err
	}
	log.Println("邮件发送成功")
	return nil
}

// 获取6位字符或者数字邮箱验证码
func GetEmailCode() string {
	const charset = "abcdefghijklmnopqrstuvwxyz0123456789"
	const length = 6
	code := make([]byte, length)
	for i := range code {
		n, _ := rand.Int(rand.Reader, big.NewInt(int64(len(charset))))
		code[i] = charset[n.Int64()]
	}
	return string(code)
}

// 生成uuid 用户利用uuid查询token，token只作为信息传递，uuid作为用户的查询键，uuid有过期时间
func GenerateUUID() (string, error) {
	// 生成UUID
	u, err := uuid.NewRandom()
	if err != nil {
		return "", err
	}
	return u.String(), nil
}

// 检查code 是否存在，以及是否一致 code 存在于redis中，并且过期时间小于当前时间
func CheckCode(code string, email string, mode string) (bool, error) {
	var result interface{}
	var err error
	if mode == "register" {
		result, err = GetRegisterCodeValue(email)

	} else {
		result, err = GetLoginCodeValue(code)
	}
	if err != nil {
		return false, err
	}
	if result == code {
		return true, nil
	}
	return false, nil
}

// 转化类型成想要的类型
func TransTypeToMyType(origin interface{}, target interface{}) error {
	jsonData, err := json.Marshal(origin)
	if err != nil {
		return err
	}
	return json.Unmarshal(jsonData, target)
}
